import os.path
import time
from datetime import datetime as datetime_
from dateutil import parser

from module.static_module.parent.model import DynamicModule
from core.constant import *
from core.object import DataServerJson
from conf import conf

from tools.framework import get_date_str, get_ui_value
from tools.file_manager import gen_data_name
from tools.data_bridge import gen_factor_val, get_mem_f_value, load_data_p2_data


class DataBridgeModel(DynamicModule):
    def __init__(self, master):
        super().__init__(master, Module.DataBridge)
        # 实体类映射视图类变量数据
        self.symbol_name_ls = []
        self.trading_variety_ls = []
        self.fighting_mode_ls = []
        self.k_interval_ls = []
        self.count_ls = []
        self.start_time_ls = []
        self.end_time_ls = []
        self.software_path_ls = []
        self.factor_name_ls = []
        self.text_box_ls = []
        # 映射视图类变量数据到实体类
        self.symbol_name = None
        self.trading_variety = None
        self.fighting_mode = None
        self.k_interval = None
        self.count = None
        self.start_time = None
        self.end_time = None
        self.software_path = None
        self.factor_name = None
        self.text_box = None
        # 必要结构变量
        self.bg = None
        self.data_dc = None
        self.is_transfer = False
        self.is_end = False
        self.modify_time = None

    def sec_init(self):
        self.symbol_name_ls = [", ".join(conf.DataBridge.symbol_name_def.value)]
        self.trading_variety_ls = [mem.value for mem in Variety]
        self.fighting_mode_ls = [mem.value for mem in DataFlowMode]
        self.k_interval_ls = conf.DataBridge.k_interval_ls.value
        self.count_ls = [0]
        self.start_time_ls = [get_date_str(30)]
        self.end_time_ls = [get_date_str()]
        self.software_path_ls = [conf.DataBridge.software_path.value]
        self.factor_name_ls = self.master.file_manager.data_def_group_dc[DataDefGroup.FName.value]
        self.text_box_ls = []
        pass

    def get_ui_params(self):
        # 获取ui界面的相关参数
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(
            LabelMember.SymbolName)
        self.symbol_name = get_ui_value(values, indices, WidgetCategory.ScrolledText)
        # 对symbol_name进行拦截处理，调整其格式，（暂不实现）
        pass
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(
            LabelMember.TradingVariety)
        self.trading_variety = get_ui_value(values, indices, WidgetCategory.Combobox)
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(
            LabelMember.FightingMode)
        self.fighting_mode = get_ui_value(values, indices, WidgetCategory.Combobox)
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(
            LabelMember.KInterval)
        self.k_interval = get_ui_value(values, indices, WidgetCategory.Combobox)
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(
            LabelMember.Count)
        self.count = get_ui_value(values, indices, WidgetCategory.Entry)
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(
            LabelMember.StartTime)
        self.start_time = get_ui_value(values, indices, WidgetCategory.Entry)
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(
            LabelMember.EndTime)
        self.end_time = get_ui_value(values, indices, WidgetCategory.Entry)
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(
            LabelMember.SoftwarePath)
        self.software_path = get_ui_value(values, indices, WidgetCategory.Entry)
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(
            LabelMember.FactorName)
        self.factor_name = get_ui_value(values, indices, WidgetCategory.CheckButton)

    def on_run_thread(self):
        try:
            # 点击运行按钮回调
            self.get_ui_params()
            # 若标的文本框为标的池csv文件
            if ".csv" in self.symbol_name:
                # 加载csv文件并将symbol name重新赋值为列表
                support_csv_name = self.symbol_name.split(',')[0]
                category_str = self.symbol_name.split(',')[1]
                if category_str:
                    category_ls = category_str.split('-')
                    support_csv_path = os.path.join(self.master.file_manager.support_path, support_csv_name)
                    support_df = self.master.file_manager.read_csv(support_csv_path)
                    selected_df = support_df[support_df["category"].isin(category_ls)]
                    symbol_arr = selected_df["code"].values
                    name_arr = selected_df["name"].values
                    self.symbol_name = symbol_arr.tolist()
                else:
                    support_csv_path = os.path.join(self.master.file_manager.support_path, support_csv_name)
                    support_df = self.master.file_manager.read_csv(support_csv_path)
                    selected_df = support_df
                    symbol_arr = selected_df["code"].values
                    name_arr = selected_df["name"].values
                    self.symbol_name = symbol_arr.tolist()

                pass
            else:
                symbol_ls = []
                for str_o in self.symbol_name.split(','):
                    symbol_ls.append(str_o.replace(' ', ''))
                self.symbol_name = symbol_ls
            # 根据software_path来区分数据来源，若正常路径则从iquant中获取
            # 否则根据具体值决定数据源，例如：值为okx，则从okx获取实时数据
            # 考虑数据桥取代数据下载模块
            if self.trading_variety == Variety.Virtual.value and self.fighting_mode == DataFlowMode.His.value:
                from tools.data_bridge import download_okx
                start_time = parser.parse(self.start_time)
                end_time = parser.parse(self.end_time)
                for symbol_o in self.symbol_name:
                    download_okx(symbol=symbol_o, k_interval=self.k_interval,
                                 start_time=start_time, end_time=end_time,
                                 file_manager=self.master.file_manager)
                self.task_end()
                return
            elif self.trading_variety == Variety.Stock.value or self.trading_variety == Variety.Future.value:
                # 配置写入data_server.json文件，并将该文件复制到数据软件下

                # json文件路径
                # 实时或历史数据文件路径（面向数据流软件的）

                # 复制json文件到软件指定路径

                # 等待数据软件拿到json文件，并将需要的数据导出
                # 监测数据软件是否生成了json文件中指定的数据文件
                if self.fighting_mode == DataFlowMode.His.value:
                    # 存储数据
                    if not self.factor_name:
                        his_data_dir = self.master.file_manager.market_data_path
                        data_name = gen_data_name(DataCategory.Mk)
                        his_data_path = os.path.join(his_data_dir, data_name + '.csv')
                    else:
                        his_data_dir = self.master.file_manager.market_factor_data_path
                        data_name = gen_data_name(DataCategory.MkFt)
                        his_data_path = os.path.join(his_data_dir, data_name + '.csv')

                    software = "DDQ Data P2"
                    data_flow_mode = get_mem_f_value(self.fighting_mode, DataFlowMode)
                    variety = get_mem_f_value(self.trading_variety, Variety)
                    symbol_ls = self.symbol_name  # 其为单元素列表，将其处理
                    # for str_o in self.symbol_name.split(','):
                    #     symbol_ls.append(str_o.replace(' ', ''))

                    interval = get_mem_f_value(self.k_interval, Interval)
                    count = int(self.count)
                    start_time = self.start_time
                    end_time = self.end_time
                    set_time = datetime_.now().strftime("%Y-%m-%d %H:%M")
                    file_name = data_name
                    data_json = DataServerJson(software, data_flow_mode, variety, symbol_ls, interval,
                                               count, start_time, end_time, set_time, file_name)
                    # 将配置以文件形式传输给软件
                    json_path = os.path.join(self.software_path, "bin.x64", "data_server.json")
                    data_json.save_json(json_path)
                    # 循环获取数据，直到成功
                    data_path = os.path.join(self.software_path, "bin.x64", file_name+".csv")
                    retry_time = 0
                    while True:
                        if self.is_end:
                            self.is_end = False
                            info = "数据监测测停止，退出当前函数。"
                            self.master.file_manager.log_engine.emit(info, LogName.Running)
                            return
                        time.sleep(3)
                        ori_data_dc = load_data_p2_data(data_path, self.master.file_manager)
                        if ori_data_dc:
                            info = "成功获取到相关历史数据。"
                            self.master.file_manager.log_engine.emit(info, LogName.Running)
                            break
                        else:
                            retry_time += 1
                            if retry_time > 2:
                                info = "超过限定重试次数，本次数据获取失败。"
                                self.master.file_manager.log_engine.emit(info, LogName.Running)
                                raise ValueError("超过限定重试次数，本次数据获取失败。")
                            info = f"未成功获取到相关历史数据，即将第{retry_time}次重试。"
                            self.master.file_manager.log_engine.emit(info, LogName.Running)

                    # 将数据进行整理，存储至行情数据文件夹，若指定因子，则附加因子生成逻辑
                    if self.factor_name:
                        # 如果有选中因子值，则计算
                        ori_data_dc = gen_factor_val(ori_data_dc, factor_ls=self.factor_name,
                                                     file_manager=self.master.file_manager)
                        pass
                    else:
                        # 没有选中因子值
                        pass
                    self.master.file_manager.save_dc_csv(ori_data_dc, his_data_path)
                elif self.fighting_mode == DataFlowMode.Live.value:
                    # 实盘数据文件夹路径
                    if not self.factor_name:
                        live_data_dir = self.master.file_manager.market_live_data_path
                        data_name = gen_data_name(DataCategory.MkLive)
                        live_data_path = os.path.join(live_data_dir, data_name + '.csv')
                    else:
                        live_data_dir = self.master.file_manager.market_factor_live_data_path
                        data_name = gen_data_name(DataCategory.MkFtLive)
                        live_data_path = os.path.join(live_data_dir, data_name + '.csv')

                    software = "DDQ Data P2"
                    data_flow_mode = get_mem_f_value(self.fighting_mode, DataFlowMode)
                    variety = get_mem_f_value(self.trading_variety, Variety)
                    # symbol_ls = []  # 其为单元素列表，将其处理
                    # for str_o in self.symbol_name.split(','):
                    #     symbol_ls.append(str_o.replace(' ', ''))
                    symbol_ls = self.symbol_name  # 其为单元素列表，将其处理
                    interval = get_mem_f_value(self.k_interval, Interval)
                    count = int(self.count)
                    start_time = self.start_time
                    end_time = self.end_time
                    set_time = datetime_.now().strftime("%Y-%m-%d %H:%M")
                    file_name = data_name
                    data_json = DataServerJson(software, data_flow_mode, variety, symbol_ls, interval,
                                         count, start_time, end_time, set_time, file_name)
                    # 将配置以文件形式传输给软件
                    json_path = os.path.join(self.software_path, "bin.x64", "data_server.json")
                    data_json.save_json(json_path)
                    # 循环获取数据。
                    data_path = os.path.join(self.software_path, "bin.x64", file_name+".csv")
                    retry_time = 0
                    while True:
                        if self.is_end:
                            self.is_end = False
                            info = "数据监测测停止，退出当前循环。"
                            self.master.file_manager.log_engine.emit(info, LogName.Running)
                            self.task_end()
                            return
                        time.sleep(3)
                        ori_data_dc = load_data_p2_data(data_path, self.master.file_manager)
                        if ori_data_dc:
                            info = "第一次成功获取到相关实时数据。"
                            self.master.file_manager.log_engine.emit(info, LogName.Running)
                            break
                        else:
                            retry_time += 1
                            if retry_time > 20:
                                info = "超过限定重试次数，本次数据获取失败。"
                                self.master.file_manager.log_engine.emit(info, LogName.Running)
                                raise ValueError("超过限定重试次数，本次数据获取失败。")
                            info = f"未成功获取到相关历史数据，即将第{retry_time}次重试。"
                            self.master.file_manager.log_engine.emit(info, LogName.Running)

                    while True:
                        if self.is_end:
                            self.is_end = False
                            info = "数据监测测停止，退出当前函数。"
                            self.master.file_manager.log_engine.emit(info, LogName.Running)
                            self.task_end()
                            return
                        if self.modify_time is None:
                            pass
                        else:
                            time.sleep(conf.DataBridge.monitor_period.value.total_seconds())
                            modify_time = datetime_.fromtimestamp(os.path.getmtime(data_path))
                            if modify_time > self.modify_time:
                                ori_data_dc = load_data_p2_data(data_path, self.master.file_manager)
                                info = "成功获取到更新的实时数据。"
                                self.master.file_manager.log_engine.emit(info, LogName.Running)
                                self.modify_time = modify_time
                            else:
                                continue
                        # 将数据进行整理，存储至行情数据文件夹，若指定因子，则附加因子生成逻辑
                        if self.factor_name:
                            # 如果有选中因子值，则计算
                            ori_data_dc = gen_factor_val(ori_data_dc,
                                                         factor_ls=self.factor_name,
                                                         file_manager=self.master.file_manager)
                            pass
                        else:
                            # 没有选中因子值
                            pass

                        # 若bridge被标记为可以传输数据
                        if self.is_transfer:
                            # 为data_dc赋值，不存储至本地
                            self.data_dc = ori_data_dc
                            self.modify_time = datetime_.fromtimestamp(os.path.getmtime(data_path))
                            pass
                        else:
                            # 为data_dc赋值，存储至本地
                            self.data_dc = ori_data_dc

                            # 存储数据
                            self.master.file_manager.save_dc_csv(ori_data_dc, live_data_path)
                            self.modify_time = datetime_.fromtimestamp(os.path.getmtime(data_path))
                else:
                    raise ValueError("未知的数据流模式，逻辑错误。")
            else:
                # 发送错误信息。
                raise ValueError("不支持的指定类型，逻辑错误。")
        except Exception as e:
            import traceback
            info = f"[{self.module.value}]任务运行出错，错误信息{e}"
            self.master.file_manager.log_engine.emit(info, LogName.Running)
            traceback.print_exc()
            pass
        self.task_end()

    def on_stop(self):
        # 修改传输标志
        self.is_transfer = False
        self.is_end = True
        info = "数据桥已停止。"
        self.master.file_manager.log_engine.emit(info, LogName.Running)

    def on_only_data_thread(self):
        # 点击运行按钮回调
        self.get_ui_params()
        data_dir_path = os.path.join(self.software_path, "bin.x64")
        # 获取目标文件夹下最新的csv文件
        data_path = ""
        for f in os.listdir(data_dir_path):
            if f.endswith('.csv') and os.path.isfile(os.path.join(data_dir_path, f)):
                temp_data_path = os.path.join(data_dir_path, f)
                if not data_path:
                    data_path = temp_data_path
                else:
                    if os.path.getmtime(temp_data_path) > os.path.getmtime(data_path):
                        data_path = temp_data_path
        if not data_path:
            info = "未获取到最新的csv文件（不存在csv文件）。"
            self.master.file_manager.log_engine.emit(info, LogName.Running)
            self.task_end()
            return

        ori_data_dc = load_data_p2_data(data_path, self.master.file_manager)
        if ori_data_dc:
            info = "成功获取到相关历史数据。"
            self.master.file_manager.log_engine.emit(info, LogName.Running)

            # 将数据进行整理，存储至行情数据文件夹，若指定因子，则附加因子生成逻辑
            if self.factor_name:
                # 如果有选中因子值，则计算
                ori_data_dc = gen_factor_val(ori_data_dc, factor_ls=self.factor_name,
                                             file_manager=self.master.file_manager)
                pass
            else:
                # 没有选中因子值
                pass
            if not self.factor_name:
                his_data_dir = self.master.file_manager.market_data_path
                data_name = gen_data_name(DataCategory.Mk)
                his_data_path = os.path.join(his_data_dir, data_name + '.csv')
            else:
                his_data_dir = self.master.file_manager.market_factor_data_path
                data_name = gen_data_name(DataCategory.MkFt)
                his_data_path = os.path.join(his_data_dir, data_name + '.csv')

            self.master.file_manager.save_dc_csv(ori_data_dc, his_data_path)
        else:
            info = "未获取到相关历史数据。"
            self.master.file_manager.log_engine.emit(info, LogName.Running)

        self.task_end()

